package nl.zuiderfestival.raydance.model;

import java.awt.Dimension;
import java.awt.Point;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Observable;
import java.util.Set;

import nl.zuiderfestival.raydance.control.RayDanceController;
import nl.zuiderfestival.raydance.model.net.ArduinoManager;
import nl.zuiderfestival.raydance.model.net.TuioEventTranslator;
import nl.zuiderfestival.raydance.model.net.RadarManager;
import nl.zuiderfestival.raydance.model.storage.RaySerializer;
import nl.zuiderfestival.raydance.model.zones.Zone;
import nl.zuiderfestival.raydance.view.RayDanceFrame;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RayDance extends Observable {
	
	private static final Logger log = LoggerFactory.getLogger(RayDance.class);
	
	private Zone selectedZone;
	private Set<Zone> zones;
	private RaySerializer storage;
	private RadarManager radarManager;
	private ArduinoManager arduinoManager;
	private TuioEventTranslator bridge;

	private int scale;

	private LocalStorage defaultConfig;
	
	public RayDance(int scale) {
		super();
		this.scale = scale;
		setSelectedZone(null);
		zones = new HashSet<Zone>();

		storage = new RaySerializer("rayZones.ray");

		defaultConfig = storage.read();
		zones = defaultConfig.getZones();
		log.info("Found "+zones.size()+" Zones in file 'rayZones.ray'");
		
		bridge = new TuioEventTranslator(this);
		radarManager = new RadarManager(bridge, defaultConfig.getRadartouchPort());
		arduinoManager = new ArduinoManager(defaultConfig.getArduinoHost(), defaultConfig.getArduinoPort());
		
		log.debug("Created.");
	}

	public void initialize() {
		for (Zone zone : zones) {
			zone.setEnabled(false);
			zone.addObserver(arduinoManager);
		}
		log.debug("ArduinoManager listens for changes on all zones");
		
		arduinoManager.start();
		radarManager.start();
		
		setChanged();
		notifyObservers(new RayEvent(RayEventType.INIT, zones));
	}

	public Zone createNewZone(int id) {
		Zone zone = new Zone(id, new Point(zones.size()*10,zones.size()*10), new Dimension(25,25));
		zones.add(zone);
		zone.addObserver(arduinoManager);
		setChanged();
		notifyObservers(new RayEvent(RayEventType.ZONEADDED, zone));
		save();
		log.info("Created new zone with id {}.", new Object[] {zone.getId()});
		return zone;
	}

	public void removeZone(Zone zone) {
		if (zones.contains(zone)) {
			zones.remove(zone);
			zone.deleteObserver(arduinoManager);
			setChanged();
			notifyObservers(new RayEvent(RayEventType.ZONEREMOVED, zone));
		}
		log.info("Removed zone with id {}.", new Object[] {zone.getId()});
		save();
	}

	public void save() {
		log.debug("Saving.");
		defaultConfig.setZones(zones);
		storage.write(defaultConfig);
		log.info("Saved current configuration.");
	}

	public Zone getSelectedZone() {
		return selectedZone;
	}

	public void setSelectedZone(Zone selectedZone) {
		this.selectedZone = selectedZone;
		if (selectedZone != null) {
			log.debug("Selected zone {}.", new Object[] {selectedZone.getId()});
		} else {
			log.debug("No zone selected.");
		}
		setChanged();
		notifyObservers(new RayEvent(RayEventType.SELECTIONCHANGED, selectedZone));
	}

	public static void main(String[] args) {
		int scale = 400;
		
		RayDance model = new RayDance(scale);
		RayDanceFrame frame = new RayDanceFrame(scale);
		new RayDanceController(model, frame);
		model.initialize();
	}

	public ArduinoManager getArduinoManager() {
		return arduinoManager;
	}

	public RadarManager getRadarManager() {
		return radarManager;
	}

	public int getScale() {
		return scale;
	}

	public List<Zone> getZonesFor(double x, double y) {
		List<Zone> result = new LinkedList<Zone>();
		for (Zone z:zones) {
			if (z.isIn(x, y)) {
				result.add(z);
			}
		}
		return result;
	}

	public void changeRadarPortTo(int port) {
		radarManager.stop();
		radarManager.changePortTo(port);
		radarManager.start();
		
		defaultConfig.setRadartouchPort(port);
		save();
	}

	public void updateArduinoConnection(String host, int port) {
		arduinoManager.stop();
		arduinoManager.updateArduinoConnection(host, port);
		arduinoManager.start();
	}
}
